# Function: create CSV record local file
# Author: Mark Xiao  2023/1/30

# API Summary:
# startReocrd()			
# 	1. No input value is needed
# 	2. Need to be called at the very beginning of the whole sequence
# createCSVRecord()		
# 	1. Need 2 input dictionaries as Input values: paraTab and limit
# 	2. Need to be called at the end of each test items to create record
# 	3. Share the same rule as Atlas to judge Pass/Fail, which is:
# 		a). when result is a value, judge it according to the upper/lower limit
# 		b). when result is a string and unit is "string", compare it with upper limit string value
# 		c). when result is a bool(string type, PASS/FAIL), judge it directly
# # finishRecord()			
# 	1. Need 1 input dictionaries to collect unit SN: paraTab
# 	2. Need to be called at the end of the whole sequence


import time
import csv
import os
from biplist import *
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

# import internal function here

# Change record and log path here
LOGPATH = "./Outputs/Logs/"


# Only for internal use
def convTime(timeStamp):
	formatTime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(timeStamp))
	return formatTime


# Only for create Record log:
def logInfo(meassage):
	path = LOGPATH + "Recordlog.txt"
	with open(path,"a") as f:
		print(meassage)
		f.write(meassage + "\r\n")


# This function should be called at the every beginning of the whole sequence
def startRecord():
	# Check log path
	if not os.path.exists(LOGPATH):
		os.mkdir(LOGPATH)

	# Create temp plist 
	plist = {}
	plist["startTime"] = time.time()
	plist["beginTest"] = convTime(plist["startTime"])
	plist["totalTime"] = 0
	plist["totalResult"] = "PASS"
	try:
		writePlist(plist, LOGPATH + "logConfig.plist")
	except (InvalidPlistException,NotBinaryPlistException) as e:
		logInfo("Write plist error: ", e)

	# Output logs:
	logInfo("Start create record successfully! Start test at " + str(plist["beginTest"]) + "\r\n")


# This function should be called once a record is beging created
def createCSVRecord(paraTab, limit):
	# paraTab including:
	# TestName:							Name for this test item
	# Input:							Test value for this test item
	# AdditionalParameters.paraName:	Subtest name for this test item
	# AdditionalParameters.failMsg:		Fail message for this test item
	# limit including:
	# upperLimit:						Upper limit for this test item
	# lowerLimit:						Lower limit for this test item
	# unit:								Unit for this test item

	# Check log path
	if not os.path.exists(LOGPATH):
		os.mkdir(LOGPATH)

	# Init all items in CSV
	testName = paraTab["TestName"]
	paraName = paraTab["AdditionalParameters"]["paraName"] or ""
	testResult = "PASS"
	testValue = paraTab["Input"] or ""
	upperLimit = limit["upperLimit"] or ""
	lowerLimit = limit["lowerLimit"] or ""
	unit = limit["unit"] or ""
	cTimeStamp = time.time()
	try:
		plist = readPlist(LOGPATH + "logConfig.plist")
		startTime = float(plist["startTime"])
	except OSError as reason:
		startTime = time.time()
		plist = {"startTime":startTime}
		logInfo("Read start time error reason:", reason)
	cTime = convTime(cTimeStamp)
	sTime = convTime(startTime)
	duration = 0
	failMsg = ""

	# Calculate test time:
	duration = cTimeStamp - startTime

	# Check itme PASS or FAIL:
	if unit == "string":
		if testValue != upperLimit:
			testResult = "FAIL"
			failMsg = paraTab["AdditionalParameters"]["failMsg"] or ""
	elif testValue == "FAIL":
		testResult = "FAIL"
		failMsg = paraTab["AdditionalParameters"]["failMsg"] or ""
	else:
		try:
			upperLimitNum = float(upperLimit)
			testValueNum = float(testValue)
			if testValueNum > upperLimitNum:
				testResult = "FAIL"
				failMsg = paraTab["AdditionalParameters"]["failMsg"] or ""
		except:
			logInfo("Upper limit or test value is not a NUM")

		try:
			lowerLimitNum = float(lowerLimit)
			testValueNum = float(testValue)
			if testValueNum < lowerLimitNum:
				testResult = "FAIL"
				failMsg = paraTab["AdditionalParameters"]["failMsg"] or ""
		except:
			logInfo("Lower limit or test value is not a NUM")

	# Create a dictionary to save record:
	record = {}
	record["TestName"] = testName + "_" + paraName
	record["Status"] = testResult
	record["Measurement"] = testValue
	record["Units"] = unit
	record["UpperLimit"] = upperLimit
	record["LowerLimit"] = lowerLimit
	record["StartTime"] = sTime
	record["EndTime"] = cTime
	record["Duration"] = duration
	record["FailMsg"] = failMsg

	logInfo("Create record as below: ")
	for key in record:
		logInfo(key + " : " + str(record[key]))
	logInfo("\r\n")

	# Create CSV log file:
	titleRow = []
	dataRow = []
	dataRow.append(record)
	for key in record:
		titleRow.append(key)

	if os.path.exists(LOGPATH + "record.csv"):
		with open(LOGPATH + "record.csv", "a", newline = '') as f:
			writer = csv.DictWriter(f, fieldnames=titleRow)
			writer.writerows(dataRow)
		logInfo("Update CSV file successfully!")
	else:
		with open(LOGPATH + "record.csv", "a", newline = '') as f:
			writer = csv.DictWriter(f, fieldnames=titleRow)
			writer.writeheader()
			writer.writerows(dataRow)
		logInfo("Create CSV file successfully!")

	# Update Plist file
	plist["startTime"] = time.time()
	if record["Status"] == "FAIL":
		plist["totalResult"] = "FAIL"
	currentTestTime = plist["totalTime"]
	totalTestTime = currentTestTime + record["Duration"]
	plist["totalTime"] = totalTestTime

	try:
		writePlist(plist, LOGPATH + "logConfig.plist")
		logInfo("Update temp plist file successfully!")
		for key in plist:
			logInfo(key + " : " + str(plist[key]))
		logInfo("\r\n")
	except (InvalidPlistException,NotBinaryPlistException) as e:
		logInfo("Write plist error: ", e)

    # Return record dictionary for other usage:
	return record


# This function should be called at the end of the whole sequence
def finishRecord(paraTab):
	# paraTab including:
	# Input:		Unit SN

	# Check log path
	if not os.path.exists(LOGPATH):
		os.mkdir(LOGPATH)

	try:
		plist = readPlist(LOGPATH + "logConfig.plist")
		beginTest = plist["beginTest"]
		totalTime = plist["totalTime"]
		totalResult = plist["totalResult"]
	except OSError as reason:
		logInfo("Read plist error reason:", reason)
	endTest = convTime(time.time())

	# Write summary into CSV record
	titleRow = ["TotalResult", totalResult, "", "", "", "", beginTest, endTest, totalTime]
	summaryRecord = {"testResult":totalResult, "BeginTime":beginTest, "EndTime":endTest, "TotalTestTime":totalTime}
	try:
		with open(LOGPATH + "record.csv", "a", newline = '') as f:
			writer = csv.DictWriter(f, fieldnames=titleRow)
			writer.writeheader()
			logInfo("Finish CSV record successfully! End at " + str(endTest))
	except (InvalidPlistException,NotBinaryPlistException) as e:
		logInfo("Write plist error: ", e)

	# Create record plot:
	file = LOGPATH + 'record.csv'
	df = pd.read_csv(file)
	labels = list(df.columns.values)

	testName = []
	testTime = []

	for index, row in df.iterrows():
		testName.append(row['TestName'])
		testTime.append(float(row['Duration']))

	testName.pop()
	testTime.pop()

	# for i in range(len(testName)):
	# 	plt.barh(testName[i], testTime[i])
	plt.figure()
	barhP = plt.subplot(2, 1, 1)
	barhP.barh(testName, testTime)
	plt.title("TT Distribution Plot")	

	# barhP.title("TT Distribution Plot")
	# barhP.xlabel("Test Time")
	# barhP.ylabel("Test Name")
	# plt.savefig(LOGPATH + "TTBreakDownBar.png")

	pieP = plt.subplot(2, 1, 2)
	pieP.pie(testTime, labels = testName)
	plt.savefig(LOGPATH + "TTBreakDown.png")

	# Rename CSV record file and delete config plist
	SN = paraTab["Input"]
	fileName = totalResult+"_"+SN+"_"+endTest+".csv"
	os.rename(LOGPATH + "record.csv", LOGPATH + fileName)
	os.remove(LOGPATH + "logConfig.plist")
	logInfo("Change record name successfully!\r\n")

	LOGPATHNEW = "./Outputs/" + totalResult+"_"+SN+"_"+endTest + "/"
	os.rename(LOGPATH, LOGPATHNEW)

    # Return summary record dictionary for other usage:
	return summaryRecord


